
/*
 * Copyright (c) 2010 Sergey N Frolov
 * 
 * See LICENSE file
 */

#include "ngx_javascript.h"

/*
 * Set socket non-blocking
 */
int ngx_javascript_server_socket_nonblock(int s) {
	int flags;
	flags = fcntl(s, F_GETFL);
	if (flags < 0) return flags;
	flags |= O_NONBLOCK;
	flags = fcntl(s, F_SETFL, flags);
	if (flags < 0) return flags;
	return 0;
}

/*
 * Create listening socket
 */
int ngx_javascript_server_socket(ngx_javascript_server_t *server) {
	int s, r, flags = 1;
	int l = sizeof(struct sockaddr_un);
	mode_t u;
	struct sockaddr_un su;
	struct sockaddr *sa = (struct sockaddr *) &su;
	su.sun_family = AF_UNIX;
	strcpy(su.sun_path, server->socket_path);
	s = socket(sa->sa_family, SOCK_STREAM, 0);
	if (s < 0) return s;
	r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
	if (r < 0) return r;
	unlink(server->socket_path);
	u = umask(0777 ^ server->socket_mode);
	r = bind(s, sa, l);
	if (r < 0) return r;
	chown(server->socket_path, server->uid, server->gid);
	umask(u);
	r = listen(s, -1);
	if (r < 0) return r;
	r = ngx_javascript_server_socket_nonblock(s);
	if (r < 0) return r;
	return s;
}

/*
 * Accept connection
 * This is called from main thread only
 */
void ngx_javascript_server_accept_event(struct ev_loop *loop, ev_io *e, int revents) {
	ngx_javascript_server_e *event = (ngx_javascript_server_e *) e;
	ngx_javascript_server_t *server = event->server;
	ngx_javascript_client_t *client;
	struct sockaddr_un client_addr;
	socklen_t client_addr_len = sizeof(struct sockaddr_un);
	
	client = ngx_javascript_server_client_slot(server);
	if (client == NULL) {
		int i;
		for (i = 0; i < 1000; i ++) {
			usleep(1000);
			client = ngx_javascript_server_client_slot(server);
			if (client != NULL) break;
		}
		if (client == NULL) {
			for (i = 0; i < 3; i ++) {
				sleep(1);
				client = ngx_javascript_server_client_slot(server);
				if (client != NULL) break;
			}
			if (client == NULL) {
				ngx_javascript_log(0, "server: no free client\n", client->n);
				return;
			}
		}
	}
	int client_fd = accept(server->fd, (struct sockaddr *) &client_addr, &client_addr_len);
	if (client_fd < 0) {
		ngx_javascript_log(0, "server: accept(): %s\n", strerror(errno));
		return;
	}
	client->fd = client_fd;
	ev_init(&client->event.io, ngx_javascript_client_read_event);
	ev_io_set(&client->event.io, client_fd, EV_READ);
	pthread_mutex_lock(&server->lock);
	ev_io_start(server->loop, &client->event.io);
	pthread_mutex_unlock(&server->lock);
}

/*
 * Server shutdown
 */
void ngx_javascript_server_shutdown(ngx_javascript_server_t *server) {
	// This is not quite clear, but works without errors
	exit(0);
}

/*
 * Handle signal
 */
void ngx_javascript_server_signal_event(struct ev_loop *loop, ev_signal *e, int revents) {
	ngx_javascript_server_t *server = ev_userdata(loop);
	ngx_javascript_server_shutdown(server);
}

void ngx_javascript_server_async_event(struct ev_loop *loop, ev_async *e, int revents) {
	int i;
	ngx_javascript_server_t *server = ev_userdata(loop);
	ngx_javascript_client_t *client;
	pthread_mutex_lock(&server->lock);
	for (i = 0; i < server->clients_len; i ++) {
		client = server->clients[i];
		if (client->status == NGX_JAVASCRIPT_STATUS_QUEUE) {
			client->status = NGX_JAVASCRIPT_STATUS_WRITE;
			ev_init(&client->event.io, ngx_javascript_client_write_event);
			ev_io_set(&client->event.io, client->fd, EV_WRITE);
			ev_io_start(server->loop, &client->event.io);
		}
	}
	pthread_mutex_unlock(&server->lock);
}
